#include "duYa_Curtain_Ctrl_Driver.h"
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "usb_hotplug_check.h"
#include "env_ctrol_service.h"

/*存储状态的参数及状态信息*/
STRUCT_DUYA_CURTAIN_INFO StruDuyaCurtainInfo = {0x00};
STRUCT_CURTAIN_CONTROL_CMDLINE struCurtainControl = {0x00};

//CRC16 计算代码
//CRC16:0xA001 余式表
//--------------------------------------------------------------
// 半字节CRC16(Dow_右移逆序) 余式表:0xA001(0x8005按位颠倒)
//--------------------------------------------------------------
uint16_t CRC16_Tab[] =
{
	0x0000, 0xCC01, 0xD801, 0x1400, 0xF001, 0x3C00, 0x2800, 0xE401,
	0xA001, 0x6C00, 0x7800, 0xB401, 0x5000, 0x9C01, 0x8801, 0x4400
};
	

/*外部接口定义*/

void duya_curtain_control_init(uint8_t channel ,uint32_t buatrate,uint8_t initstate)
{

	LOGD("curtain control init ");
	
	memset(&StruDuyaCurtainInfo,0,sizeof(StruDuyaCurtainInfo));
	memset(&struCurtainControl,0,sizeof (STRUCT_CURTAIN_CONTROL_CMDLINE));

	StruDuyaCurtainInfo.serialbuatrate = buatrate;
	StruDuyaCurtainInfo.serialchannelid = channel;

	StruDuyaCurtainInfo.serialinitok = initstate;
	return ;
}

/**
*将要发送的数据进行打包 :
*参数 deviceid :设备地址 
	  funcode :功能码
	  srcdata: 要发送的数据长度
	  srcdatalen ：要发送的数据
	  destdata : 打包之后要发送的的数据
*返回值 : 打包之后 要发送的数据帧长度
*
*/
uint8_t duya_curtain_control_pack_data(uint16_t deviceid,uint8_t funcode,uint8_t dataadd,uint8_t srcdatalen,int32_t* srcdata,uint8_t * destdata)
{

	uint8_t i = 0x00;
	
	uint8_t cnt = 0x00;
	uint16_t tmpcrc = 0x00;

	uint32_t *tmptr = (uint32_t*)srcdata;

//#if DEBUG_DUYE_CURTAIN_CONTROL_ENABLE == 0X01
//		uint8_t tmp[2] = {0x00};
//
//		LOGD("duya curtain control pack data : srcdata :");
//		tmp[0] = srcdata[0];
//		LogOutHexString(tmp,1);
//		tmp[1] = srcdata[1];
//	    LogOutHexString(tmp + 1,1);
//#endif

	/*获取以下参数 */
	/* 起始码 ：0x55*/
	destdata[cnt++] = CURTAIN_CONTROL_START_CODE;

	/*设备地址 : 2个字节:先发送地址底8位 在发送地址高8位*/
	destdata[cnt++] = deviceid & 0xff;
	destdata[cnt++] = (deviceid >>8) & 0xff;

	/*功能码  : 1个字节*/
	destdata[cnt++] = funcode;

	/*数据地址: 1个字节*/
	destdata[cnt++] = dataadd;

	switch(funcode)
	{
		/*读*/
		case CURTAIN_READ_FUNCODE:
		{
			if (srcdatalen > 0x00)
			{
				/*要读取的数据长度*/
				destdata[cnt++] = srcdatalen;
			}
		}
		break;

		/*写*/
		case CURTAIN_WRITE_FUNCODE:
		{
			/*要写的数据长度*/
			if (srcdatalen > 0x01)
			{
				destdata[cnt++] = srcdatalen;
			}

			/*将要发送的数据存储到缓冲区*/
			for (i = 0x00;i < srcdatalen;i ++)
			{
				/*要写的数据*/
				destdata[cnt++] = tmptr[i] & 0x000000ff;
			}
		}
		break;

		/*控制*/
		case CURTAIN_CONTROL_FUNCODE:
		{
			/*要写的数据长度*/
			if (srcdatalen > 0x01)
			{
				destdata[cnt++] = srcdatalen;
			}

			/*将要发送的数据存储到缓冲区*/
			for (i = 0x00;i < srcdatalen;i ++)
			{
				/*要写的数据*/
				destdata[cnt++] = tmptr[i] & 0x000000ff;
			}
		}
		break;

		/*从机请求指令*/
		case CURTAIN_SLAVE_REQUEST_FUNCODE:
		{
				/*暂时不支持从机请求指令*/
		}
		break;
		
		default :break;
	}

	/*校验位  : 2个字节  crc校验*/
	tmpcrc = Get_CRC16(destdata,cnt);
	cnt += 2;	

	/*crc value*/
	destdata[cnt - 2] = tmpcrc ;	
	destdata[cnt - 1] = tmpcrc >> 8;

	return cnt;
	
}


/********************************************************************************
*函数 ：curtain_control_unpack_data
*说明：解析接收到的数据
*参数 
*	  pdata :接收到的数据
*	  len：接收到的数据长度
*	  deviceid :设备地址 
*	  funcode :功能码
*	  dataadd :已发送数据的起始数据地址
*	  num :发送的数据长度
*返回值 : 接收到的数据长度 ，接收成功为>0 ,接收失败为0
*
*********************************************************************************/
uint8_t duya_curtain_control_unpack_data(uint8_t *pdata,uint8_t len,uint16_t devid,uint8_t func,uint8_t dataadd,uint8_t num)
{

	uint8_t  i 		= 0x00;
	uint8_t  cnt 	= 0x00;

	uint8_t  nbyte 	= 0x00;

	uint16_t tmpcrc = 0x00;
	uint16_t calcrc = 0x00;


	int tmp = 0x00;

	/*接收帧长度大于最小帧长度*/
	if (len < CURTAIN_CONTROL_RECV_FRAME_MINI_LENGTH)
	{
		return 0;
	}

	/*起始码 ： 0x55*/
	if (pdata[cnt ++] != CURTAIN_CONTROL_START_CODE)
	{
		return 0x00;
	}
	
	/*crc校验*/
	tmpcrc = pdata[len - 2 ] | (pdata[len - 1 ] << 8);
	calcrc = Get_CRC16(pdata,len - 2);

	if (calcrc != tmpcrc)
	{
		return 0;
	}

	
	/*发送帧的设备地址 :作为一帧数据发送之后的等待接收帧标记记录*/
	if (devid != (pdata[cnt] |(pdata[cnt + 1] << 8)))
	{
		return 0;
	}
	
	cnt += 2;
	StruDuyaCurtainInfo.devid = devid;

	/*功能码字段 :注意：删除情景模式中的控制码 为0x03 但设备回复的控制码为0x01, 发送和接收到的控制码不一致 ，
	*			   其他指令发出的控制码和收到的控制码相同。
	*/
	if ((func != pdata[cnt ++]))
	{
		return 0;
	}

#if DEBUG_DUYE_CURTAIN_CONTROL_ENABLE == 0X01
	LOGD("duya curtain control data receive ok!");
#endif

	switch(func)
	{
		/*读*/
		case CURTAIN_READ_FUNCODE:
		{
			/*接收到的数据长度 :*/
			nbyte = pdata[cnt ++];
			
			/*将接收到的数据存储到缓冲字段*/
			for(i = 0;i < nbyte;i ++)
			{
				StruDuyaCurtainInfo.curtaininfo[i + dataadd] = pdata[cnt++];
			}
			
		}
		break;

		/*写*/
		case CURTAIN_WRITE_FUNCODE:
		{
			/*接收到的数据长度 :*/
			nbyte = pdata[cnt ++];
		}
		break;

		/*控制*/
		case CURTAIN_CONTROL_FUNCODE:
		{
			/*控制命令中的数据地址*/
			if (dataadd != pdata[cnt++])
			{
				/*接收到的控制数据地址 和 已发送的数据地址不一致*/
			}
		}
		break;

		/*从机请求指令:从机发送指令主动请求配置设备地址, 从机默认设备地址为 0xfefe*/
		case CURTAIN_SLAVE_REQUEST_FUNCODE:
		{
			/*暂时不支持从机请求指令*/
		}
		break;
		
		default :break;
		
	}

	cnt = len;
	return (cnt);
}


/********************************************************************************
*函数 ：curtain_control_read_data
*说明：将接收的数据传递到上层
*参数 
*	  pdata :接收到的数据
*	  len：接收到的数据长度
*	  deviceid :设备地址 
*	  funcode :功能码
*	  dataadd :已发送数据的起始数据地址
*	  num :发送的数据长度
*返回值 : 接收到的数据长度 ，接收成功为>0 ,接收失败为0
*
*********************************************************************************/
uint8_t duya_curtain_control_read_data(uint16_t deviceid,uint8_t funcode,uint8_t dataadd,uint8_t srcdatalen,uint8_t * destdata)
{

	uint8_t  i 		= 0x00;
	uint8_t  cnt 	= 0x00;

	int * pdata = (int *)destdata;

	/*当前存储的数据是否是要读取的设备的信息*/
	if (deviceid != StruDuyaCurtainInfo.devid)
	{
		return 0x00;
	}

#if DEBUG_DUYE_CURTAIN_CONTROL_ENABLE == 0X01
			StruDuyaCurtainInfo.curtaininfo[0] = 0x20;
			StruDuyaCurtainInfo.curtaininfo[1] = 0x35;

			LOGD("devid :%d,dataadd :%d,srcdatalen :%d,funcode: %d",deviceid,dataadd,srcdatalen,funcode);
#endif

	switch(funcode)
	{
		/*读*/
		case CURTAIN_READ_FUNCODE:
		{

			/*将接收到的数据存储到缓冲字段*/
			for(i = 0;i < srcdatalen;i ++)
			{
				pdata[cnt ++] = StruDuyaCurtainInfo.curtaininfo[i + dataadd] & 0x000000ff;
			}

		}
		break;

		/*写*/
		case CURTAIN_WRITE_FUNCODE:
		{
		}
		break;

		/*控制*/
		case CURTAIN_CONTROL_FUNCODE:
		{
		}
		break;

		/*从机请求指令:从机发送指令主动请求配置设备地址, 从机默认设备地址为 0xfefe*/
		case CURTAIN_SLAVE_REQUEST_FUNCODE:
		{
			/*暂时不支持从机请求指令*/
		}
		break;
		
		default :break;
		
	}

	return (cnt);
}


/***********************************************************
*
*函数名:
*
*作用：设备控制通道
*
*	
*
*返回值 :0x00
************************************************************/
uint8_t duya_curtain_get_cmd_channel(uint8_t * channel)
{
	uint8_t ret = 0x00;
	if (StruDuyaCurtainInfo.serialinitok != USB_SERIAL_DEVICE_INIT_OK)
	{
		ret = 0x01;
	}
	else
	{
		if (StruDuyaCurtainInfo.serialchannelid > USB_SERIAL_AMOUNT)
		{
			ret = 0x01;
		}
		else
		{
			*channel = StruDuyaCurtainInfo.serialchannelid ;
		}
	}

	return ret;
}

/////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////

//--------------------------------------------------------------
// 函数名称: Calc_Crc16
// 功能描述: 计算单个字节的CRC16
// 输入参数: 输入字节,crc16初始值
// 返回参数: 无
// 修改记录:
// 2015-01-11, PiaoHong 创建函数
//---------------------------------------------------------------
uint16_t Calc_Crc16(uint8_t crcbuf, uint16_t crc_16)
{
uint8_t temp = (uint8_t) (((uint8_t) (crc_16 & 0x000F)) ^ (crcbuf & 0x0F));
crc_16 >>= 4;
crc_16 ^= CRC16_Tab[temp];
temp = ((uint8_t)(crc_16&0x000F))^(crcbuf>>4);
crc_16 >>= 4;
crc_16 ^= CRC16_Tab[temp];
return crc_16;
}
//--------------------------------------------------------------
// 函数名称: Get_CRC16_byGID
// 功能描述: 得到CRC16
// 输入参数: 待进行CRC校验计算的报文 待校验的报文长度
// 返回参数: CRC16
// 修改记录:
// 2015-01-11, PiaoHong 创建函数
//---------------------------------------------------------------
uint16_t Get_CRC16(uint8_t *pBuff, uint16_t len)
{
uint8_t i;
uint16_t crc16 = 0xFFFF;
for (i=0;i<len;i++)
{
crc16 = Calc_Crc16(*pBuff++,crc16);
}
return crc16;
}



